﻿#include "StdAfx.h"

namespace DuiLib {

	/////////////////////////////////////////////////////////////////////////////////////
	//
	//

	class CActiveXCtrl;


	/////////////////////////////////////////////////////////////////////////////////////
	//
	//

	class CActiveXWnd: public CWindowWnd {
	public:
		CActiveXWnd (): m_iLayeredTick (0), m_bDrawCaret (false) {}
		HWND Init (CActiveXCtrl* pOwner, HWND hWndParent);

		faw::string_view_t GetWindowClassName () const;
		void OnFinalMessage (HWND hWnd);

		LRESULT HandleMessage (UINT uMsg, WPARAM wParam, LPARAM lParam);

	protected:
		void DoVerb (LONG iVerb);

		LRESULT OnCreate (UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
		LRESULT OnTimer (UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
		LRESULT OnMouseActivate (UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
		LRESULT OnSetFocus (UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
		LRESULT OnKillFocus (UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
		LRESULT OnEraseBkgnd (UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
		LRESULT OnPaint (UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
		LRESULT OnPrint (UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);

	protected:
		enum {
			DEFAULT_TIMERID = 20,
		};

		CActiveXCtrl* m_pOwner;
		int m_iLayeredTick;
		bool m_bDrawCaret;
	};


	/////////////////////////////////////////////////////////////////////////////////////
	//
	//

	class CActiveXEnum: public IEnumUnknown {
	public:
		CActiveXEnum (IUnknown* pUnk): m_pUnk (pUnk), m_dwRef (1), m_iPos (0) {
			m_pUnk->AddRef ();
		}
		virtual ~CActiveXEnum () {
			m_pUnk->Release ();
		}

		LONG m_iPos;
		ULONG m_dwRef;
		IUnknown* m_pUnk;

		STDMETHOD_ (ULONG, AddRef)() {
			return ++m_dwRef;
		}
		STDMETHOD_ (ULONG, Release)() {
			LONG lRef = --m_dwRef;
			if (lRef == 0) delete this;
			return lRef;
		}
		STDMETHOD (QueryInterface)(REFIID riid, LPVOID *ppvObject) {
			*ppvObject = nullptr;
			if (riid == IID_IUnknown) *ppvObject = static_cast<IEnumUnknown*>(this);
			else if (riid == IID_IEnumUnknown) *ppvObject = static_cast<IEnumUnknown*>(this);
			if (*ppvObject) AddRef ();
			return (!*ppvObject) ? E_NOINTERFACE : S_OK;
		}
		STDMETHOD (Next)(ULONG celt, IUnknown **rgelt, ULONG *pceltFetched) {
			if (pceltFetched) *pceltFetched = 0;
			if (++m_iPos > 1) return S_FALSE;
			*rgelt = m_pUnk;
			(*rgelt)->AddRef ();
			if (pceltFetched) *pceltFetched = 1;
			return S_OK;
		}
		STDMETHOD (Skip)(ULONG celt) {
			m_iPos += celt;
			return S_OK;
		}
		STDMETHOD (Reset)(void) {
			m_iPos = 0;
			return S_OK;
		}
		STDMETHOD (Clone)(IEnumUnknown **ppenum) {
			return E_NOTIMPL;
		}
	};


	/////////////////////////////////////////////////////////////////////////////////////
	//
	//

	class CActiveXFrameWnd: public IOleInPlaceFrame {
	public:
		CActiveXFrameWnd (CActiveXUI* pOwner): m_dwRef (1), m_pOwner (pOwner), m_pActiveObject (nullptr) {}
		virtual ~CActiveXFrameWnd () {
			if (m_pActiveObject) m_pActiveObject->Release ();
		}

		ULONG m_dwRef;
		CActiveXUI* m_pOwner;
		IOleInPlaceActiveObject* m_pActiveObject;

		// IUnknown
		STDMETHOD_ (ULONG, AddRef)() {
			return ++m_dwRef;
		}
		STDMETHOD_ (ULONG, Release)() {
			ULONG lRef = --m_dwRef;
			if (lRef == 0) delete this;
			return lRef;
		}
		STDMETHOD (QueryInterface)(REFIID riid, LPVOID *ppvObject) {
			*ppvObject = nullptr;
			if (riid == IID_IUnknown) *ppvObject = static_cast<IOleInPlaceFrame*>(this);
			else if (riid == IID_IOleWindow) *ppvObject = static_cast<IOleWindow*>(this);
			else if (riid == IID_IOleInPlaceFrame) *ppvObject = static_cast<IOleInPlaceFrame*>(this);
			else if (riid == IID_IOleInPlaceUIWindow) *ppvObject = static_cast<IOleInPlaceUIWindow*>(this);
			if (*ppvObject) AddRef ();
			return (!*ppvObject) ? E_NOINTERFACE : S_OK;
		}
		// IOleInPlaceFrameWindow
		STDMETHOD (InsertMenus)(HMENU /*hmenuShared*/, LPOLEMENUGROUPWIDTHS /*lpMenuWidths*/) {
			return S_OK;
		}
		STDMETHOD (SetMenu)(HMENU /*hmenuShared*/, HOLEMENU /*holemenu*/, HWND /*hwndActiveObject*/) {
			return S_OK;
		}
		STDMETHOD (RemoveMenus)(HMENU /*hmenuShared*/) {
			return S_OK;
		}
		STDMETHOD (SetStatusText)(LPCOLESTR /*pszStatusText*/) {
			return S_OK;
		}
		STDMETHOD (EnableModeless)(BOOL /*fEnable*/) {
			return S_OK;
		}
		STDMETHOD (TranslateAccelerator)(LPMSG /*lpMsg*/, WORD /*wID*/) {
			return S_FALSE;
		}
		// IOleWindow
		STDMETHOD (GetWindow)(HWND* phwnd) {
			if (!m_pOwner) return E_UNEXPECTED;
			*phwnd = m_pOwner->GetManager ()->GetPaintWindow ();
			return S_OK;
		}
		STDMETHOD (ContextSensitiveHelp)(BOOL /*fEnterMode*/) {
			return S_OK;
		}
		// IOleInPlaceUIWindow
		STDMETHOD (GetBorder)(LPRECT /*lprectBorder*/) {
			return S_OK;
		}
		STDMETHOD (RequestBorderSpace)(LPCBORDERWIDTHS /*pborderwidths*/) {
			return INPLACE_E_NOTOOLSPACE;
		}
		STDMETHOD (SetBorderSpace)(LPCBORDERWIDTHS /*pborderwidths*/) {
			return S_OK;
		}
		STDMETHOD (SetActiveObject)(IOleInPlaceActiveObject* pActiveObject, LPCOLESTR /*pszObjName*/) {
			if (pActiveObject) pActiveObject->AddRef ();
			if (m_pActiveObject) m_pActiveObject->Release ();
			m_pActiveObject = pActiveObject;
			return S_OK;
		}
	};

	/////////////////////////////////////////////////////////////////////////////////////
	//

	class CActiveXCtrl:
		public IOleClientSite,
		public IOleInPlaceSiteWindowless,
		public IOleControlSite,
		public IObjectWithSite,
		public IOleContainer {
		friend class CActiveXUI;
		friend class CActiveXWnd;
	public:
		CActiveXCtrl ();
		virtual ~CActiveXCtrl ();

		// IUnknown
		STDMETHOD_ (ULONG, AddRef)();
		STDMETHOD_ (ULONG, Release)();
		STDMETHOD (QueryInterface)(REFIID riid, LPVOID *ppvObject);

		// IObjectWithSite
		STDMETHOD (SetSite)(IUnknown *pUnkSite);
		STDMETHOD (GetSite)(REFIID riid, LPVOID* ppvSite);

		// IOleClientSite
		STDMETHOD (SaveObject)(void);
		STDMETHOD (GetMoniker)(DWORD dwAssign, DWORD dwWhichMoniker, IMoniker** ppmk);
		STDMETHOD (GetContainer)(IOleContainer** ppContainer);
		STDMETHOD (ShowObject)(void);
		STDMETHOD (OnShowWindow)(BOOL fShow);
		STDMETHOD (RequestNewObjectLayout)(void);

		// IOleInPlaceSiteWindowless
		STDMETHOD (CanWindowlessActivate)(void);
		STDMETHOD (GetCapture)(void);
		STDMETHOD (SetCapture)(BOOL fCapture);
		STDMETHOD (GetFocus)(void);
		STDMETHOD (SetFocus)(BOOL fFocus);
		STDMETHOD (GetDC)(LPCRECT pRect, DWORD grfFlags, HDC* phDC);
		STDMETHOD (ReleaseDC)(HDC hDC);
		STDMETHOD (InvalidateRect)(LPCRECT pRect, BOOL fErase);
		STDMETHOD (InvalidateRgn)(HRGN hRGN, BOOL fErase);
		STDMETHOD (ScrollRect)(INT dx, INT dy, LPCRECT pRectScroll, LPCRECT pRectClip);
		STDMETHOD (AdjustRect)(LPRECT prc);
		STDMETHOD (OnDefWindowMessage)(UINT msg, WPARAM wParam, LPARAM lParam, LRESULT* plResult);

		// IOleInPlaceSiteEx
		STDMETHOD (OnInPlaceActivateEx)(BOOL *pfNoRedraw, DWORD dwFlags);
		STDMETHOD (OnInPlaceDeactivateEx)(BOOL fNoRedraw);
		STDMETHOD (RequestUIActivate)(void);

		// IOleInPlaceSite
		STDMETHOD (CanInPlaceActivate)(void);
		STDMETHOD (OnInPlaceActivate)(void);
		STDMETHOD (OnUIActivate)(void);
		STDMETHOD (GetWindowContext)(IOleInPlaceFrame** ppFrame, IOleInPlaceUIWindow** ppDoc, LPRECT lprcPosRect, LPRECT lprcClipRect, LPOLEINPLACEFRAMEINFO lpFrameInfo);
		STDMETHOD (Scroll)(SIZE scrollExtant);
		STDMETHOD (OnUIDeactivate)(BOOL fUndoable);
		STDMETHOD (OnInPlaceDeactivate)(void);
		STDMETHOD (DiscardUndoState)(void);
		STDMETHOD (DeactivateAndUndo)(void);
		STDMETHOD (OnPosRectChange)(LPCRECT lprcPosRect);

		// IOleWindow
		STDMETHOD (GetWindow)(HWND* phwnd);
		STDMETHOD (ContextSensitiveHelp)(BOOL fEnterMode);

		// IOleControlSite
		STDMETHOD (OnControlInfoChanged)(void);
		STDMETHOD (LockInPlaceActive)(BOOL fLock);
		STDMETHOD (GetExtendedControl)(IDispatch** ppDisp);
		STDMETHOD (TransformCoords)(POINTL* pPtlHimetric, POINTF* pPtfContainer, DWORD dwFlags);
		STDMETHOD (TranslateAccelerator)(MSG* pMsg, DWORD grfModifiers);
		STDMETHOD (OnFocus)(BOOL fGotFocus);
		STDMETHOD (ShowPropertyFrame)(void);

		// IOleContainer
		STDMETHOD (EnumObjects)(DWORD grfFlags, IEnumUnknown** ppenum);
		STDMETHOD (LockContainer)(BOOL fLock);

		// IParseDisplayName
		STDMETHOD (ParseDisplayName)(IBindCtx* pbc, LPOLESTR pszDisplayName, ULONG* pchEaten, IMoniker** ppmkOut);

	protected:
		HRESULT CreateActiveXWnd ();

	protected:
		LONG m_dwRef;
		CActiveXUI* m_pOwner;
		CActiveXWnd* m_pWindow;
		IUnknown* m_pUnkSite;
		IViewObject* m_pViewObject;
		IOleInPlaceObjectWindowless* m_pInPlaceObject;
		bool m_bLocked;
		bool m_bFocused;
		bool m_bCaptured;
		bool m_bUIActivated;
		bool m_bInPlaceActive;
		bool m_bWindowless;
	};

	CActiveXCtrl::CActiveXCtrl (): m_pOwner (nullptr), m_pWindow (nullptr), m_pUnkSite (nullptr), m_pViewObject (nullptr), m_pInPlaceObject (nullptr) {
		m_dwRef = 1;
		m_bLocked = m_bFocused = m_bCaptured = m_bWindowless = m_bUIActivated = m_bInPlaceActive = false;
	}

	CActiveXCtrl::~CActiveXCtrl () {
		if (m_pWindow) {
			::DestroyWindow (m_pWindow->GetHWND ());
			delete m_pWindow;
		}
		if (m_pUnkSite) m_pUnkSite->Release ();
		if (m_pViewObject) m_pViewObject->Release ();
		if (m_pInPlaceObject) m_pInPlaceObject->Release ();
	}

	STDMETHODIMP CActiveXCtrl::QueryInterface (REFIID riid, LPVOID *ppvObject) {
		*ppvObject = nullptr;
		if (riid == IID_IUnknown)                       *ppvObject = static_cast<IOleWindow*>(this);
		else if (riid == IID_IOleClientSite)            *ppvObject = static_cast<IOleClientSite*>(this);
		else if (riid == IID_IOleInPlaceSiteWindowless) *ppvObject = static_cast<IOleInPlaceSiteWindowless*>(this);
		else if (riid == IID_IOleInPlaceSiteEx)         *ppvObject = static_cast<IOleInPlaceSiteEx*>(this);
		else if (riid == IID_IOleInPlaceSite)           *ppvObject = static_cast<IOleInPlaceSite*>(this);
		else if (riid == IID_IOleWindow)                *ppvObject = static_cast<IOleWindow*>(this);
		else if (riid == IID_IOleControlSite)           *ppvObject = static_cast<IOleControlSite*>(this);
		else if (riid == IID_IOleContainer)             *ppvObject = static_cast<IOleContainer*>(this);
		else if (riid == IID_IObjectWithSite)           *ppvObject = static_cast<IObjectWithSite*>(this);
		if (*ppvObject) AddRef ();
		return (!*ppvObject) ? E_NOINTERFACE : S_OK;
	}

	STDMETHODIMP_ (ULONG) CActiveXCtrl::AddRef () {
		return ++m_dwRef;
	}

	STDMETHODIMP_ (ULONG) CActiveXCtrl::Release () {
		LONG lRef = --m_dwRef;
		if (lRef == 0) delete this;
		return lRef;
	}

	STDMETHODIMP CActiveXCtrl::SetSite (IUnknown *pUnkSite) {
		DUITRACE (_T ("AX: CActiveXCtrl::SetSite"));
		if (m_pUnkSite) {
			m_pUnkSite->Release ();
			m_pUnkSite = nullptr;
		}
		if (pUnkSite) {
			m_pUnkSite = pUnkSite;
			m_pUnkSite->AddRef ();
		}
		return S_OK;
	}

	STDMETHODIMP CActiveXCtrl::GetSite (REFIID riid, LPVOID* ppvSite) {
		DUITRACE (_T ("AX: CActiveXCtrl::GetSite"));
		if (!ppvSite) return E_POINTER;
		*ppvSite = nullptr;
		if (!m_pUnkSite) return E_FAIL;
		return m_pUnkSite->QueryInterface (riid, ppvSite);
	}

	STDMETHODIMP CActiveXCtrl::SaveObject (void) {
		DUITRACE (_T ("AX: CActiveXCtrl::SaveObject"));
		return E_NOTIMPL;
	}

	STDMETHODIMP CActiveXCtrl::GetMoniker (DWORD dwAssign, DWORD dwWhichMoniker, IMoniker** ppmk) {
		DUITRACE (_T ("AX: CActiveXCtrl::GetMoniker"));
		if (ppmk) *ppmk = nullptr;
		return E_NOTIMPL;
	}

	STDMETHODIMP CActiveXCtrl::GetContainer (IOleContainer** ppContainer) {
		DUITRACE (_T ("AX: CActiveXCtrl::GetContainer"));
		if (!ppContainer) return E_POINTER;
		*ppContainer = nullptr;
		HRESULT Hr = E_NOTIMPL;
		if (m_pUnkSite) Hr = m_pUnkSite->QueryInterface (IID_IOleContainer, (LPVOID*) ppContainer);
		if (FAILED (Hr)) Hr = QueryInterface (IID_IOleContainer, (LPVOID*) ppContainer);
		return Hr;
	}

	STDMETHODIMP CActiveXCtrl::ShowObject (void) {
		DUITRACE (_T ("AX: CActiveXCtrl::ShowObject"));
		if (!m_pOwner) return E_UNEXPECTED;
		HDC hDC = ::GetDC (m_pOwner->m_hwndHost);
		if (hDC == NULL) return E_FAIL;
		if (m_pViewObject) m_pViewObject->Draw (DVASPECT_CONTENT, -1, nullptr, nullptr, NULL, hDC, (RECTL*) &m_pOwner->m_rcItem, (RECTL*) &m_pOwner->m_rcItem, nullptr, NULL);
		::ReleaseDC (m_pOwner->m_hwndHost, hDC);
		return S_OK;
	}

	STDMETHODIMP CActiveXCtrl::OnShowWindow (BOOL fShow) {
		DUITRACE (_T ("AX: CActiveXCtrl::OnShowWindow"));
		return E_NOTIMPL;
	}

	STDMETHODIMP CActiveXCtrl::RequestNewObjectLayout (void) {
		DUITRACE (_T ("AX: CActiveXCtrl::RequestNewObjectLayout"));
		return E_NOTIMPL;
	}

	STDMETHODIMP CActiveXCtrl::CanWindowlessActivate (void) {
		DUITRACE (_T ("AX: CActiveXCtrl::CanWindowlessActivate"));
		return S_OK;  // Yes, we can!!
	}

	STDMETHODIMP CActiveXCtrl::GetCapture (void) {
		DUITRACE (_T ("AX: CActiveXCtrl::GetCapture"));
		if (!m_pOwner) return E_UNEXPECTED;
		return m_bCaptured ? S_OK : S_FALSE;
	}

	STDMETHODIMP CActiveXCtrl::SetCapture (BOOL fCapture) {
		DUITRACE (_T ("AX: CActiveXCtrl::SetCapture"));
		if (!m_pOwner) return E_UNEXPECTED;
		m_bCaptured = (fCapture == TRUE);
		if (fCapture) ::SetCapture (m_pOwner->m_hwndHost); else ::ReleaseCapture ();
		return S_OK;
	}

	STDMETHODIMP CActiveXCtrl::GetFocus (void) {
		DUITRACE (_T ("AX: CActiveXCtrl::GetFocus"));
		if (!m_pOwner) return E_UNEXPECTED;
		return m_bFocused ? S_OK : S_FALSE;
	}

	STDMETHODIMP CActiveXCtrl::SetFocus (BOOL fFocus) {
		DUITRACE (_T ("AX: CActiveXCtrl::SetFocus"));
		if (!m_pOwner) return E_UNEXPECTED;
		if (fFocus) m_pOwner->SetFocus ();
		m_bFocused = (fFocus == TRUE);
		return S_OK;
	}

	STDMETHODIMP CActiveXCtrl::GetDC (LPCRECT pRect, DWORD grfFlags, HDC* phDC) {
		DUITRACE (_T ("AX: CActiveXCtrl::GetDC"));
		if (!phDC) return E_POINTER;
		if (!m_pOwner) return E_UNEXPECTED;
		if (m_bWindowless) return S_FALSE;
		*phDC = ::GetDC (m_pOwner->m_hwndHost);
		if ((grfFlags & OLEDC_PAINTBKGND) != 0) {
			RECT rcItem = m_pOwner->GetPos ();
			if (!m_bWindowless) ::OffsetRect (&rcItem, -rcItem.left, -rcItem.top);
			::FillRect (*phDC, &rcItem, (HBRUSH) (COLOR_WINDOW + 1));
		}
		return S_OK;
	}

	STDMETHODIMP CActiveXCtrl::ReleaseDC (HDC hDC) {
		DUITRACE (_T ("AX: CActiveXCtrl::ReleaseDC"));
		if (!m_pOwner) return E_UNEXPECTED;
		::ReleaseDC (m_pOwner->m_hwndHost, hDC);
		return S_OK;
	}

	STDMETHODIMP CActiveXCtrl::InvalidateRect (LPCRECT pRect, BOOL fErase) {
		DUITRACE (_T ("AX: CActiveXCtrl::InvalidateRect"));
		if (!m_pOwner) return E_UNEXPECTED;
		if (!m_pOwner->m_hwndHost) return E_FAIL;
		return ::InvalidateRect (m_pOwner->m_hwndHost, pRect, fErase) ? S_OK : E_FAIL;
	}

	STDMETHODIMP CActiveXCtrl::InvalidateRgn (HRGN hRGN, BOOL fErase) {
		DUITRACE (_T ("AX: CActiveXCtrl::InvalidateRgn"));
		if (!m_pOwner) return E_UNEXPECTED;
		return ::InvalidateRgn (m_pOwner->m_hwndHost, hRGN, fErase) ? S_OK : E_FAIL;
	}

	STDMETHODIMP CActiveXCtrl::ScrollRect (INT dx, INT dy, LPCRECT pRectScroll, LPCRECT pRectClip) {
		DUITRACE (_T ("AX: CActiveXCtrl::ScrollRect"));
		return S_OK;
	}

	STDMETHODIMP CActiveXCtrl::AdjustRect (LPRECT prc) {
		DUITRACE (_T ("AX: CActiveXCtrl::AdjustRect"));
		return S_OK;
	}

	STDMETHODIMP CActiveXCtrl::OnDefWindowMessage (UINT msg, WPARAM wParam, LPARAM lParam, LRESULT* plResult) {
		DUITRACE (_T ("AX: CActiveXCtrl::OnDefWindowMessage"));
		if (!m_pOwner) return E_UNEXPECTED;
		*plResult = ::DefWindowProc (m_pOwner->m_hwndHost, msg, wParam, lParam);
		return S_OK;
	}

	STDMETHODIMP CActiveXCtrl::OnInPlaceActivateEx (BOOL* pfNoRedraw, DWORD dwFlags) {
		DUITRACE (_T ("AX: CActiveXCtrl::OnInPlaceActivateEx"));
		ASSERT (!m_pInPlaceObject);
		if (!m_pOwner) return E_UNEXPECTED;
		if (!m_pOwner->m_pUnk) return E_UNEXPECTED;
		::OleLockRunning (m_pOwner->m_pUnk, TRUE, FALSE);
		HWND hWndFrame = m_pOwner->GetManager ()->GetPaintWindow ();
		HRESULT Hr = E_FAIL;
		if ((dwFlags & ACTIVATE_WINDOWLESS) != 0) {
			m_bWindowless = true;
			Hr = m_pOwner->m_pUnk->QueryInterface (IID_IOleInPlaceObjectWindowless, (LPVOID*) &m_pInPlaceObject);
			m_pOwner->m_hwndHost = hWndFrame;
			m_pOwner->GetManager ()->AddMessageFilter (m_pOwner);
		}
		if (FAILED (Hr)) {
			m_bWindowless = false;
			Hr = CreateActiveXWnd ();
			if (FAILED (Hr)) return Hr;
			Hr = m_pOwner->m_pUnk->QueryInterface (IID_IOleInPlaceObject, (LPVOID*) &m_pInPlaceObject);
		}
		if (m_pInPlaceObject && !m_pOwner->IsMFC ()) {
			RECT rcItem = m_pOwner->m_rcItem;
			if (!m_bWindowless) ::OffsetRect (&rcItem, -rcItem.left, -rcItem.top);
			m_pInPlaceObject->SetObjectRects (&rcItem, &rcItem);
		}
		m_bInPlaceActive = SUCCEEDED (Hr);
		return Hr;
	}

	STDMETHODIMP CActiveXCtrl::OnInPlaceDeactivateEx (BOOL fNoRedraw) {
		DUITRACE (_T ("AX: CActiveXCtrl::OnInPlaceDeactivateEx"));
		m_bInPlaceActive = false;
		if (m_pInPlaceObject) {
			m_pInPlaceObject->Release ();
			m_pInPlaceObject = nullptr;
		}
		if (m_pWindow) {
			::DestroyWindow (m_pWindow->GetHWND ());
			delete m_pWindow;
			m_pWindow = nullptr;
		}
		return S_OK;
	}

	STDMETHODIMP CActiveXCtrl::RequestUIActivate (void) {
		DUITRACE (_T ("AX: CActiveXCtrl::RequestUIActivate"));
		return S_OK;
	}

	STDMETHODIMP CActiveXCtrl::CanInPlaceActivate (void) {
		DUITRACE (_T ("AX: CActiveXCtrl::CanInPlaceActivate"));
		return S_OK;
	}

	STDMETHODIMP CActiveXCtrl::OnInPlaceActivate (void) {
		DUITRACE (_T ("AX: CActiveXCtrl::OnInPlaceActivate"));
		BOOL bDummy = FALSE;
		return OnInPlaceActivateEx (&bDummy, 0);
	}

	STDMETHODIMP CActiveXCtrl::OnUIActivate (void) {
		DUITRACE (_T ("AX: CActiveXCtrl::OnUIActivate"));
		m_bUIActivated = true;
		return S_OK;
	}

	STDMETHODIMP CActiveXCtrl::GetWindowContext (IOleInPlaceFrame** ppFrame, IOleInPlaceUIWindow** ppDoc, LPRECT lprcPosRect, LPRECT lprcClipRect, LPOLEINPLACEFRAMEINFO lpFrameInfo) {
		DUITRACE (_T ("AX: CActiveXCtrl::GetWindowContext"));
		if (!ppDoc) return E_POINTER;
		if (!ppFrame) return E_POINTER;
		if (!lprcPosRect) return E_POINTER;
		if (!lprcClipRect) return E_POINTER;
		if (m_pWindow) {
			::GetClientRect (m_pWindow->GetHWND (), lprcPosRect);
			::GetClientRect (m_pWindow->GetHWND (), lprcClipRect);
		}
		*ppFrame = new CActiveXFrameWnd (m_pOwner);
		*ppDoc = nullptr;
		ACCEL ac = { 0 };
		HACCEL hac = ::CreateAcceleratorTable (&ac, 1);
		lpFrameInfo->cb = sizeof (OLEINPLACEFRAMEINFO);
		lpFrameInfo->fMDIApp = FALSE;
		lpFrameInfo->hwndFrame = m_pOwner->GetManager ()->GetPaintWindow ();
		lpFrameInfo->haccel = hac;
		lpFrameInfo->cAccelEntries = 1;
		return S_OK;
	}

	STDMETHODIMP CActiveXCtrl::Scroll (SIZE scrollExtant) {
		DUITRACE (_T ("AX: CActiveXCtrl::Scroll"));
		return E_NOTIMPL;
	}

	STDMETHODIMP CActiveXCtrl::OnUIDeactivate (BOOL fUndoable) {
		DUITRACE (_T ("AX: CActiveXCtrl::OnUIDeactivate"));
		m_bUIActivated = false;
		return S_OK;
	}

	STDMETHODIMP CActiveXCtrl::OnInPlaceDeactivate (void) {
		DUITRACE (_T ("AX: CActiveXCtrl::OnInPlaceDeactivate"));
		return OnInPlaceDeactivateEx (TRUE);
	}

	STDMETHODIMP CActiveXCtrl::DiscardUndoState (void) {
		DUITRACE (_T ("AX: CActiveXCtrl::DiscardUndoState"));
		return E_NOTIMPL;
	}

	STDMETHODIMP CActiveXCtrl::DeactivateAndUndo (void) {
		DUITRACE (_T ("AX: CActiveXCtrl::DeactivateAndUndo"));
		return E_NOTIMPL;
	}

	STDMETHODIMP CActiveXCtrl::OnPosRectChange (LPCRECT lprcPosRect) {
		DUITRACE (_T ("AX: CActiveXCtrl::OnPosRectChange"));
		return E_NOTIMPL;
	}

	STDMETHODIMP CActiveXCtrl::GetWindow (HWND* phwnd) {
		DUITRACE (_T ("AX: CActiveXCtrl::GetWindow"));
		if (!m_pOwner) return E_UNEXPECTED;
		if (!m_pOwner->m_hwndHost) {
			CreateActiveXWnd ();
			return E_FAIL;
		}
		*phwnd = m_pOwner->m_hwndHost;
		return S_OK;
	}

	STDMETHODIMP CActiveXCtrl::ContextSensitiveHelp (BOOL fEnterMode) {
		DUITRACE (_T ("AX: CActiveXCtrl::ContextSensitiveHelp"));
		return S_OK;
	}

	STDMETHODIMP CActiveXCtrl::OnControlInfoChanged (void) {
		DUITRACE (_T ("AX: CActiveXCtrl::OnControlInfoChanged"));
		return S_OK;
	}

	STDMETHODIMP CActiveXCtrl::LockInPlaceActive (BOOL fLock) {
		DUITRACE (_T ("AX: CActiveXCtrl::LockInPlaceActive"));
		return S_OK;
	}

	STDMETHODIMP CActiveXCtrl::GetExtendedControl (IDispatch** ppDisp) {
		DUITRACE (_T ("AX: CActiveXCtrl::GetExtendedControl"));
		if (!ppDisp) return E_POINTER;
		if (!m_pOwner) return E_UNEXPECTED;
		if (!m_pOwner->m_pUnk) return E_UNEXPECTED;
		return m_pOwner->m_pUnk->QueryInterface (IID_IDispatch, (LPVOID*) ppDisp);
	}

	STDMETHODIMP CActiveXCtrl::TransformCoords (POINTL* pPtlHimetric, POINTF* pPtfContainer, DWORD dwFlags) {
		DUITRACE (_T ("AX: CActiveXCtrl::TransformCoords"));
		return S_OK;
	}

	STDMETHODIMP CActiveXCtrl::TranslateAccelerator (MSG *pMsg, DWORD grfModifiers) {
		DUITRACE (_T ("AX: CActiveXCtrl::TranslateAccelerator"));
		return S_FALSE;
	}

	STDMETHODIMP CActiveXCtrl::OnFocus (BOOL fGotFocus) {
		DUITRACE (_T ("AX: CActiveXCtrl::OnFocus"));
		m_bFocused = (fGotFocus == TRUE);
		return S_OK;
	}

	STDMETHODIMP CActiveXCtrl::ShowPropertyFrame (void) {
		DUITRACE (_T ("AX: CActiveXCtrl::ShowPropertyFrame"));
		return E_NOTIMPL;
	}

	STDMETHODIMP CActiveXCtrl::EnumObjects (DWORD grfFlags, IEnumUnknown** ppenum) {
		DUITRACE (_T ("AX: CActiveXCtrl::EnumObjects"));
		if (!ppenum) return E_POINTER;
		if (!m_pOwner) return E_UNEXPECTED;
		*ppenum = new CActiveXEnum (m_pOwner->m_pUnk);
		return S_OK;
	}

	STDMETHODIMP CActiveXCtrl::LockContainer (BOOL fLock) {
		DUITRACE (_T ("AX: CActiveXCtrl::LockContainer"));
		m_bLocked = fLock != FALSE;
		return S_OK;
	}

	STDMETHODIMP CActiveXCtrl::ParseDisplayName (IBindCtx *pbc, LPOLESTR pszDisplayName, ULONG* pchEaten, IMoniker** ppmkOut) {
		DUITRACE (_T ("AX: CActiveXCtrl::ParseDisplayName"));
		return E_NOTIMPL;
	}

	HRESULT CActiveXCtrl::CreateActiveXWnd () {
		if (m_pWindow) return S_OK;
		m_pWindow = new CActiveXWnd;
		if (!m_pWindow) return E_OUTOFMEMORY;
		m_pOwner->m_hwndHost = m_pWindow->Init (this, m_pOwner->GetManager ()->GetPaintWindow ());
		return S_OK;
	}


	/////////////////////////////////////////////////////////////////////////////////////
	//
	//

	HWND CActiveXWnd::Init (CActiveXCtrl* pOwner, HWND hWndParent) {
		m_pOwner = pOwner;
		UINT uStyle = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
		Create (hWndParent, _T ("UIActiveX"), uStyle, 0L, 0, 0, 0, 0, NULL);
		return m_hWnd;
	}

	faw::string_view_t CActiveXWnd::GetWindowClassName () const {
		return _T ("ActiveXWnd");
	}

	void CActiveXWnd::OnFinalMessage (HWND hWnd) {
		if (m_pOwner->m_pOwner->GetManager ()->IsLayered ()) {
			m_pOwner->m_pOwner->GetManager ()->RemoveNativeWindow (hWnd);
		}
	}

	void CActiveXWnd::DoVerb (LONG iVerb) {
		if (!m_pOwner) return;
		if (!m_pOwner->m_pOwner) return;
		IOleObject* pUnk = nullptr;
		m_pOwner->m_pOwner->GetControl (IID_IOleObject, (LPVOID*) &pUnk);
		if (!pUnk) return;
		CSafeRelease<IOleObject> RefOleObject = pUnk;
		IOleClientSite* pOleClientSite = nullptr;
		m_pOwner->QueryInterface (IID_IOleClientSite, (LPVOID*) &pOleClientSite);
		CSafeRelease<IOleClientSite> RefOleClientSite = pOleClientSite;
		pUnk->DoVerb (iVerb, nullptr, pOleClientSite, 0, m_hWnd, &m_pOwner->m_pOwner->GetPos ());
	}

	LRESULT CActiveXWnd::HandleMessage (UINT uMsg, WPARAM wParam, LPARAM lParam) {
		LRESULT lRes = 0;
		BOOL bHandled = TRUE;
		switch (uMsg) {
		case WM_CREATE:        lRes = OnCreate (uMsg, wParam, lParam, bHandled); break;
		case WM_TIMER:         lRes = OnTimer (uMsg, wParam, lParam, bHandled); break;
		case WM_PAINT:         lRes = OnPaint (uMsg, wParam, lParam, bHandled); break;
		case WM_PRINT:		   lRes = OnPrint (uMsg, wParam, lParam, bHandled); break;
		case WM_SETFOCUS:      lRes = OnSetFocus (uMsg, wParam, lParam, bHandled); break;
		case WM_KILLFOCUS:     lRes = OnKillFocus (uMsg, wParam, lParam, bHandled); break;
		case WM_ERASEBKGND:    lRes = OnEraseBkgnd (uMsg, wParam, lParam, bHandled); break;
		case WM_MOUSEACTIVATE: lRes = OnMouseActivate (uMsg, wParam, lParam, bHandled); break;
		case WM_MOUSEWHEEL: break;
		default:
			bHandled = FALSE;
		}
		if (!bHandled) return CWindowWnd::HandleMessage (uMsg, wParam, lParam);
		return lRes;
	}

	LRESULT CActiveXWnd::OnCreate (UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) {
		if (m_pOwner->m_pOwner->GetManager ()->IsLayered ()) {
			::SetTimer (m_hWnd, CARET_TIMERID, ::GetCaretBlinkTime (), nullptr);
		}
		return 0;
	}

	LRESULT CActiveXWnd::OnTimer (UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) {
		if (wParam == DEFAULT_TIMERID) {
			if (m_pOwner->m_pOwner->GetManager ()->IsLayered ()) {
				//m_pOwner->m_pOwner->GetManager()->AddNativeWindow(m_pOwner->m_pOwner, m_hWnd);
				m_iLayeredTick += 1;
				if (m_iLayeredTick >= 10) {
					m_iLayeredTick = 0;
					m_bDrawCaret = !m_bDrawCaret;
				}
			}
			return 0;
		}
		bHandled = FALSE;
		return 0;
	}

	LRESULT CActiveXWnd::OnEraseBkgnd (UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) {
		if (!m_pOwner->m_pViewObject) bHandled = FALSE;
		return 1;
	}

	LRESULT CActiveXWnd::OnMouseActivate (UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) {
		IOleObject* pUnk = nullptr;
		m_pOwner->m_pOwner->GetControl (IID_IOleObject, (LPVOID*) &pUnk);
		if (!pUnk) return 0;
		CSafeRelease<IOleObject> RefOleObject = pUnk;
		DWORD dwMiscStatus = 0;
		pUnk->GetMiscStatus (DVASPECT_CONTENT, &dwMiscStatus);
		if ((dwMiscStatus & OLEMISC_NOUIACTIVATE) != 0) return 0;
		if (!m_pOwner->m_bInPlaceActive) DoVerb (OLEIVERB_INPLACEACTIVATE);
		bHandled = FALSE;
		return 0;
	}

	LRESULT CActiveXWnd::OnSetFocus (UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) {
		bHandled = FALSE;
		m_pOwner->m_bFocused = true;
		if (!m_pOwner->m_bUIActivated) DoVerb (OLEIVERB_UIACTIVATE);
		return 0;
	}

	LRESULT CActiveXWnd::OnKillFocus (UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) {
		bHandled = FALSE;
		m_pOwner->m_bFocused = false;
		return 0;
	}

	LRESULT CActiveXWnd::OnPaint (UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) {
		PAINTSTRUCT ps = { 0 };
		::BeginPaint (m_hWnd, &ps);
		::EndPaint (m_hWnd, &ps);
		return 1;
	}

	LRESULT CActiveXWnd::OnPrint (UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) {
		RECT rcClient = { 0 };
		::GetClientRect (m_hWnd, &rcClient);
		m_pOwner->m_pViewObject->Draw (DVASPECT_CONTENT, -1, nullptr, nullptr, NULL, (HDC) wParam, (RECTL*) &rcClient, nullptr, nullptr, NULL);

		if (m_bDrawCaret) {
			RECT rcPos = m_pOwner->m_pOwner->GetPos ();
			GUITHREADINFO guiThreadInfo;
			guiThreadInfo.cbSize = sizeof (GUITHREADINFO);
			::GetGUIThreadInfo (0, &guiThreadInfo);
			if (guiThreadInfo.hwndCaret) {
				POINT ptCaret = { 0 };
				ptCaret.x = guiThreadInfo.rcCaret.left;
				ptCaret.y = guiThreadInfo.rcCaret.top;
				::ClientToScreen (guiThreadInfo.hwndCaret, &ptCaret);
				::ScreenToClient (m_pOwner->m_pOwner->GetManager ()->GetPaintWindow (), &ptCaret);
				if (::PtInRect (&rcPos, ptCaret)) {
					RECT rcCaret = { 0 };
					rcCaret = guiThreadInfo.rcCaret;
					rcCaret.right = rcCaret.left;
					CRenderEngine::DrawLine ((HDC) wParam, rcCaret, 1, 0xFF000000);
				}
			}
		}

		return 1;
	}

	/////////////////////////////////////////////////////////////////////////////////////
	//
	//
	IMPLEMENT_DUICONTROL (CActiveXUI)

	CActiveXUI::CActiveXUI () {}

	CActiveXUI::~CActiveXUI () {
		ReleaseControl ();
	}

	faw::string_view_t CActiveXUI::GetClass () const {
		return _T ("ActiveXUI");
	}

	LPVOID CActiveXUI::GetInterface (faw::string_view_t pstrName) {
		if (pstrName == DUI_CTRL_ACTIVEX) return static_cast<CActiveXUI*>(this);
		return CControlUI::GetInterface (pstrName);
	}

	HWND CActiveXUI::GetHostWindow () const {
		return m_hwndHost;
	}

	static void PixelToHiMetric (const SIZEL* lpSizeInPix, LPSIZEL lpSizeInHiMetric) {
#define HIMETRIC_PER_INCH   2540
#define MAP_PIX_TO_LOGHIM(x,ppli)   MulDiv(HIMETRIC_PER_INCH, (x), (ppli))
#define MAP_LOGHIM_TO_PIX(x,ppli)   MulDiv((ppli), (x), HIMETRIC_PER_INCH)
		int nPixelsPerInchX;    // Pixels per logical inch along width
		int nPixelsPerInchY;    // Pixels per logical inch along height
		HDC hDCScreen = ::GetDC (NULL);
		nPixelsPerInchX = ::GetDeviceCaps (hDCScreen, LOGPIXELSX);
		nPixelsPerInchY = ::GetDeviceCaps (hDCScreen, LOGPIXELSY);
		::ReleaseDC (NULL, hDCScreen);
		lpSizeInHiMetric->cx = MAP_PIX_TO_LOGHIM (lpSizeInPix->cx, nPixelsPerInchX);
		lpSizeInHiMetric->cy = MAP_PIX_TO_LOGHIM (lpSizeInPix->cy, nPixelsPerInchY);
	}

	void CActiveXUI::SetVisible (bool bVisible) {
		CControlUI::SetVisible (bVisible);
		if (m_hwndHost != NULL && !m_pControl->m_bWindowless)
			::ShowWindow (m_hwndHost, IsVisible () ? SW_SHOW : SW_HIDE);
	}

	void CActiveXUI::SetInternVisible (bool bVisible) {
		CControlUI::SetInternVisible (bVisible);
		if (m_hwndHost != NULL && !m_pControl->m_bWindowless)
			::ShowWindow (m_hwndHost, IsVisible () ? SW_SHOW : SW_HIDE);
	}

	void CActiveXUI::SetPos (RECT rc, bool bNeedInvalidate) {
		CControlUI::SetPos (rc, bNeedInvalidate);

		if (!m_bCreated) DoCreateControl ();

		if (!m_pUnk) return;
		if (!m_pControl) return;

		SIZEL hmSize = { 0 };
		SIZEL pxSize = { 0 };
		pxSize.cx = m_rcItem.right - m_rcItem.left;
		pxSize.cy = m_rcItem.bottom - m_rcItem.top;
		PixelToHiMetric (&pxSize, &hmSize);

		if (m_pUnk) {
			m_pUnk->SetExtent (DVASPECT_CONTENT, &hmSize);
		}
		if (m_pControl->m_pInPlaceObject) {
			RECT rcItem = m_rcItem;
			if (!m_pControl->m_bWindowless) ::OffsetRect (&rcItem, -rcItem.left, -rcItem.top);
			m_pControl->m_pInPlaceObject->SetObjectRects (&rcItem, &rcItem);
		}
		if (!m_pControl->m_bWindowless) {
			ASSERT (m_pControl->m_pWindow);
			::MoveWindow (m_pControl->m_pWindow->GetHWND (), m_rcItem.left, m_rcItem.top, m_rcItem.right - m_rcItem.left, m_rcItem.bottom - m_rcItem.top, TRUE);
		}
	}

	void CActiveXUI::Move (SIZE szOffset, bool bNeedInvalidate) {
		CControlUI::Move (szOffset, bNeedInvalidate);
		if (!m_pControl->m_bWindowless) {
			ASSERT (m_pControl->m_pWindow);
			::MoveWindow (m_pControl->m_pWindow->GetHWND (), m_rcItem.left, m_rcItem.top, m_rcItem.right - m_rcItem.left, m_rcItem.bottom - m_rcItem.top, TRUE);
		}
	}

	bool CActiveXUI::DoPaint (HDC hDC, const RECT& rcPaint, CControlUI* pStopControl) {
		if (m_pControl && m_pControl->m_bWindowless && m_pControl->m_pViewObject) {
			m_pControl->m_pViewObject->Draw (DVASPECT_CONTENT, -1, nullptr, nullptr, NULL, hDC, (RECTL*) &m_rcItem, (RECTL*) &m_rcItem, nullptr, NULL);
		}
		return true;
	}

	void CActiveXUI::SetAttribute (faw::string_view_t pstrName, faw::string_view_t pstrValue) {
		if (pstrName == _T ("clsid")) CreateControl (pstrValue);
		else if (pstrName == _T ("modulename")) SetModuleName (pstrValue);
		else if (pstrName == _T ("delaycreate")) SetDelayCreate (FawTools::parse_bool (pstrValue));
		else CControlUI::SetAttribute (pstrName, pstrValue);
	}

	LRESULT CActiveXUI::MessageHandler (UINT uMsg, WPARAM wParam, LPARAM lParam, bool& bHandled) {
		if (!m_pControl) return 0;
		ASSERT (m_pControl->m_bWindowless);
		if (!m_pControl->m_bInPlaceActive) return 0;
		if (!m_pControl->m_pInPlaceObject) return 0;
		if (!IsMouseEnabled () && uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST) return 0;
		bool bWasHandled = true;
		if ((uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST) || uMsg == WM_SETCURSOR) {
			// Mouse message only go when captured or inside rect
			DWORD dwHitResult = m_pControl->m_bCaptured ? HITRESULT_HIT : HITRESULT_OUTSIDE;
			if (dwHitResult == HITRESULT_OUTSIDE && m_pControl->m_pViewObject) {
				IViewObjectEx* pViewEx = nullptr;
				m_pControl->m_pViewObject->QueryInterface (IID_IViewObjectEx, (LPVOID*) &pViewEx);
				if (pViewEx) {
					POINT ptMouse = { GET_X_LPARAM (lParam), GET_Y_LPARAM (lParam) };
					pViewEx->QueryHitPoint (DVASPECT_CONTENT, &m_rcItem, ptMouse, 0, &dwHitResult);
					pViewEx->Release ();
				}
			}
			if (dwHitResult != HITRESULT_HIT) return 0;
			if (uMsg == WM_SETCURSOR) bWasHandled = false;
		} else if (uMsg >= WM_KEYFIRST && uMsg <= WM_KEYLAST) {
			// Keyboard messages just go when we have focus
			if (!IsFocused ()) return 0;
		} else {
			switch (uMsg) {
			case WM_HELP:
			case WM_CONTEXTMENU:
				bWasHandled = false;
				break;
			default:
				return 0;
			}
		}
		LRESULT lResult = 0;
		HRESULT Hr = m_pControl->m_pInPlaceObject->OnWindowMessage (uMsg, wParam, lParam, &lResult);
		if (Hr == S_OK) bHandled = bWasHandled;
		return lResult;
	}

	bool CActiveXUI::IsDelayCreate () const {
		return m_bDelayCreate;
	}

	void CActiveXUI::SetDelayCreate (bool bDelayCreate) {
		if (m_bDelayCreate == bDelayCreate) return;
		if (bDelayCreate == false) {
			if (m_bCreated == false && m_clsid != IID_NULL) DoCreateControl ();
		}
		m_bDelayCreate = bDelayCreate;
	}

	bool CActiveXUI::IsMFC () const {
		return m_bMFC;
	}

	void CActiveXUI::SetMFC (bool bMFC/* = false*/) {
		if (m_bMFC == bMFC) return;
		m_bMFC = bMFC;
	}

	bool CActiveXUI::CreateControl (faw::string_view_t pstrCLSID) {
		CLSID clsid = { 0 };
		OLECHAR szCLSID[100] = { 0 };
#ifndef _UNICODE
		::MultiByteToWideChar (::GetACP (), 0, pstrCLSID.data (), -1, szCLSID, lengthof (szCLSID) - 1);
#else
		_tcsncpy (szCLSID, pstrCLSID.data (), lengthof (szCLSID) - 1);
#endif
		if (pstrCLSID[0] == '{') ::CLSIDFromString (szCLSID, &clsid);
		else ::CLSIDFromProgID (szCLSID, &clsid);
		return CreateControl (clsid);
	}

	bool CActiveXUI::CreateControl (const CLSID clsid) {
		ASSERT (clsid != IID_NULL);
		if (clsid == IID_NULL) return false;
		m_bCreated = false;
		m_clsid = clsid;
		if (!m_bDelayCreate) DoCreateControl ();
		return true;
	}

	void CActiveXUI::ReleaseControl () {
		// 移除消息链
		if (m_pManager) m_pManager->RemoveMessageFilter (this);

		if (m_pUnk) {
			IObjectWithSite* pSite = nullptr;
			m_pUnk->QueryInterface (IID_IObjectWithSite, (LPVOID*) &pSite);
			if (pSite) {
				pSite->SetSite (nullptr);
				pSite->Release ();
			}
			if (!IsMFC ()) {
				m_pUnk->Close (OLECLOSE_NOSAVE);
			}
			m_pUnk->SetClientSite (nullptr);
			m_pUnk->Release ();
			m_pUnk = nullptr;
		}
		// 销毁CActiveXCtrl
		if (m_pControl) {
			m_pControl->m_pOwner = nullptr;
			m_pControl->Release ();
			m_pControl = nullptr;
		}

		m_hwndHost = NULL;
	}

	typedef HRESULT (__stdcall *DllGetClassObjectFunc)(REFCLSID rclsid, REFIID riid, LPVOID* ppv);

	bool CActiveXUI::DoCreateControl () {
		ReleaseControl ();
		// At this point we'll create the ActiveX control
		m_bCreated = true;
		IOleControl* pOleControl = nullptr;

		HRESULT Hr = -1;
		if (!m_sModuleName.empty ()) {
			HMODULE hModule = ::LoadLibrary (m_sModuleName.c_str ());
			if (hModule) {
				IClassFactory* aClassFactory = nullptr;
				DllGetClassObjectFunc aDllGetClassObjectFunc = (DllGetClassObjectFunc)::GetProcAddress (hModule, "DllGetClassObject");
				Hr = aDllGetClassObjectFunc (m_clsid, IID_IClassFactory, (LPVOID*) &aClassFactory);
				if (SUCCEEDED (Hr)) {
					Hr = aClassFactory->CreateInstance (nullptr, IID_IOleObject, (LPVOID*) &pOleControl);
				}
				aClassFactory->Release ();
			}
		}
		if (FAILED (Hr)) {
			Hr = ::CoCreateInstance (m_clsid, nullptr, CLSCTX_ALL, IID_IOleControl, (LPVOID*) &pOleControl);
		}
		ASSERT (SUCCEEDED (Hr));
		if (FAILED (Hr)) return false;
		pOleControl->QueryInterface (IID_IOleObject, (LPVOID*) &m_pUnk);
		pOleControl->Release ();
		if (!m_pUnk) return false;
		// Create the host too
		m_pControl = new CActiveXCtrl ();
		m_pControl->m_pOwner = this;
		// More control creation stuff
		DWORD dwMiscStatus = 0;
		m_pUnk->GetMiscStatus (DVASPECT_CONTENT, &dwMiscStatus);
		IOleClientSite* pOleClientSite = nullptr;
		m_pControl->QueryInterface (IID_IOleClientSite, (LPVOID*) &pOleClientSite);
		CSafeRelease<IOleClientSite> RefOleClientSite = pOleClientSite;
		// Initialize control
		if ((dwMiscStatus & OLEMISC_SETCLIENTSITEFIRST) != 0) m_pUnk->SetClientSite (pOleClientSite);
		IPersistStreamInit* pPersistStreamInit = nullptr;
		m_pUnk->QueryInterface (IID_IPersistStreamInit, (LPVOID*) &pPersistStreamInit);
		if (pPersistStreamInit) {
			Hr = pPersistStreamInit->InitNew ();
			pPersistStreamInit->Release ();
		}
		if (FAILED (Hr)) return false;
		if ((dwMiscStatus & OLEMISC_SETCLIENTSITEFIRST) == 0) m_pUnk->SetClientSite (pOleClientSite);
		// Grab the view...
		Hr = m_pUnk->QueryInterface (IID_IViewObjectEx, (LPVOID*) &m_pControl->m_pViewObject);
		if (FAILED (Hr)) Hr = m_pUnk->QueryInterface (IID_IViewObject2, (LPVOID*) &m_pControl->m_pViewObject);
		if (FAILED (Hr)) Hr = m_pUnk->QueryInterface (IID_IViewObject, (LPVOID*) &m_pControl->m_pViewObject);
		// Activate and done...
		m_pUnk->SetHostNames (OLESTR ("UIActiveX"), nullptr);
		if (m_pManager) m_pManager->SendNotify ((CControlUI*) this, DUI_MSGTYPE_SHOWACTIVEX, 0, 0, false);
		if ((dwMiscStatus & OLEMISC_INVISIBLEATRUNTIME) == 0) {
			try {
				if (m_pManager) Hr = m_pUnk->DoVerb (OLEIVERB_INPLACEACTIVATE, nullptr, pOleClientSite, 0, m_pManager->GetPaintWindow (), &m_rcItem);
			} catch (...) {
			}
		}
		IObjectWithSite* pSite = nullptr;
		m_pUnk->QueryInterface (IID_IObjectWithSite, (LPVOID*) &pSite);
		if (pSite) {
			pSite->SetSite (static_cast<IOleClientSite*>(m_pControl));
			pSite->Release ();
		}
		return SUCCEEDED (Hr);
	}

	HRESULT CActiveXUI::GetControl (const IID iid, LPVOID* ppRet) {
		ASSERT (ppRet);
		ASSERT (!*ppRet);
		if (!ppRet) return E_POINTER;
		if (!m_pUnk) return E_PENDING;
		return m_pUnk->QueryInterface (iid, (LPVOID*) ppRet);
	}

	CLSID CActiveXUI::GetClisd () const {
		return m_clsid;
	}

	faw::string_view_t CActiveXUI::GetModuleName () const {
		return m_sModuleName.str_view ();
	}

	void CActiveXUI::SetModuleName (faw::string_view_t pstrText) {
		m_sModuleName = pstrText;
	}

} // namespace DuiLib